#include "pch.hpp"
#include "Window.hpp"
#include "WidgetImpl.hpp"
#include "WinException.hpp"

using namespace WinUi;




Widget::Widget(Widget* parent, const std::wstring& className, int2 pos, int2 size, int style)
	: d(new impl(className))
	, m_pos(pos)
	, m_size(size)

	, destroyed(m_destroyed)
{
	d->wnd = CreateWindow(d->wc.lpszClassName, L"Unnamed",
						  style, pos.x, pos.y, size.x, size.y,
						  parent ? parent->d->wnd : NULL, NULL, d->wc.hInstance, NULL);
	if(!d->wnd)
		BOOSTEXT_THROW(WinApiException("CreateWindow(d->wc.lpszClassName, L\"Unnamed\", style, pos.x, pos.y, size.x, size.y, parent ? parent->d->hWnd : NULL, NULL, d->wc.hInstance, NULL) failed"));

	updateClientSize();

	// Just to be certain that this doesn't cause undefined behaviour
	BOOST_STATIC_ASSERT(sizeof(LONG_PTR) == sizeof(void*));
	SetWindowLongPtr(d->wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Widget::~Widget()
{
	// Test if this is still a window...
	// if it is, destroy it
	if(IsWindow(d->wnd))
	{
		WIN_API(DestroyWindow(d->wnd));
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////




Widget* Widget::parent() const
{
	HWND parent = GetParent(d->wnd);
	if(!parent) return NULL;

	return reinterpret_cast<Widget*>(GetWindowLongPtr(parent, GWLP_USERDATA));
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::setParent(Widget *parent)
{
	SetParent(d->wnd, parent->d->wnd);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




int2 Widget::pos() const
{
	return m_pos;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int2 Widget::size() const
{
	return m_size;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int2 Widget::clientSize() const
{
	return m_clientSize;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void Widget::setText(const std::wstring& name)
{
	WIN_API(SetWindowText(d->wnd, name.c_str()));
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::setPos(int2 pos)
{
	WIN_API(SetWindowPos(d->wnd, NULL, pos.x, pos.y, 0, 0, SWP_NOZORDER | SWP_NOSIZE));
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::setPos(int x, int y)
{
	setPos(int2(x, y));
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::setSize(int2 size)
{
	WIN_API(SetWindowPos(d->wnd, NULL, 0, 0, size.x, size.y, SWP_NOZORDER | SWP_NOREPOSITION));
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::setSize(int width, int height)
{
	setSize(int2(width, height));
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void Widget::show()
{
	// ShowWindow does not indicate if the function succeeded
	ShowWindow(d->wnd, SW_SHOW);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::hide()
{
	// ShowWindow does not indicate if the function succeeded
	ShowWindow(d->wnd, SW_HIDE);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




bool Widget::enabled() const
{
	// If the window is enabled, the return value is nonzero.
	return IsWindowEnabled(d->wnd) != 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Widget::disabled() const
{
	// If the window is not enabled, the return value is zero.
	return IsWindowEnabled(d->wnd) == 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::enable(bool b)
{
	EnableWindow(d->wnd, b);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::disable(bool b)
{
	EnableWindow(d->wnd, !b);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




bool Widget::visible() const
{
	// If the specified window, its parent window, its parent's parent window, and so forth,
	// have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero.
	// Because the return value specifies whether the window has the WS_VISIBLE style, it may be nonzero
	// even if the window is totally obscured by other windows.
	return IsWindowVisible(d->wnd) != 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




LRESULT Widget::msgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_MOVE:
		{
			// The WM_MOVE message is sent after a window has been moved.
			m_pos.x = LOWORD(lParam);
			m_pos.y = HIWORD(lParam);

			return 0;
		}
	case WM_SIZE:
		{
			// The WM_SIZE message is sent to a window after its size has changed.
			m_size.x = LOWORD(lParam);
			m_size.y = HIWORD(lParam);
			updateClientSize();

			return 0;
		}
	case WM_CLOSE:
		{
			// The WM_CLOSE message is sent as a signal that a window or an application should terminate.
			m_destroyed();
			WIN_API(DestroyWindow(d->wnd));

			return 0;
		}
	default:
		{
			// All other messages are uninteresting: ignore them
			return DefWindowProc(d->wnd, msg, wParam, lParam);
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Widget::updateClientSize()
{
	RECT rect;
	WIN_API(GetClientRect(d->wnd, &rect));
	m_clientSize.x = rect.right - rect.left;
	m_clientSize.y = rect.bottom - rect.top;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
